/**
 * @author Jacye.Ouyang
 */
/*dependency: jquery-1.3.2.min.js*/
/*dependency: jquery.json.min.js*/
/*dependency: indicatorCalculattion.js*/
/*dependency:chartUtil.js*/
if(typeof(QSAPI)=="undefined") var QSAPI={};
if(typeof(QSAPI.DataManager)=="undefined") QSAPI.DataManager = {};

if(typeof(PULL_Path)=="undefined"){
    PULL_Path="//pullqs.morningstar.com/pullservice/pull/";
}
var RTQ_ChartDataManager = function(config){
	this.subscribeID = "QSAPI.DataManager.Chart";
	this.dataUrls = {
		intraday:"quote.jsp?",
		historical:"getStockHistoricalData.jsp?",
		fundamental:"getFundamentalData.jsp?",
		minutelyIntraday:"getMinutelyIntradayData.jsp?",
		exportDataToExcel:"exportChartDataToExcel.jsp?",
		fundHistorical:"getFundChartData.jsp?",
		pullDelayedData:PULL_Path+"getPullDelayedChartData.jsp?",
        forex:"getForexHistoricalData.jsp?"
	};
	this.dataSourceType = "push";
	this.crossDomain= false;
	this.dataDomain="";
	this.subUSExchange = [41,11,9,30,29,33,22,25];  // indexes exchange need get intraday chart from sub exchange.
	this.option = {
		needUpdateIntradyData:false,           //provide a switch for 3td chart API if or not connect to push server to refresh the intraday chart real-time.
		maximumOfCachedSymblos:30              // the maximum of cached symbols data.
	}
	this._fundamentNameMap = {HS0A2:"PE",HS0A5:"PS",HS0A0:"PB",HS0A1:"PC",HS987:"FairValue",STA0C:"SInterest",ST263:"REPS",HS793:"MReturnIndex"};
	this._fundamentCodeMap = {PE:"HS0A2",PS:"HS0A5",PB:"HS0A0",PC:"HS0A1",FairValue:"HS987",Earnings:"QS001",SInterest:"STA0C",REPS:"ST263",MReturnIndex:"HS793"};
	this.quoteDataMap = {};
	this.gkeyPIdMap = {};
	//this.sessionId = sessionId;
	this.registeredSymbols = {};//{"0P000002RH":[obj,obj]}
	this._currentDate = new Date();
	this.dates = { 
		1:new Date(this._currentDate.getFullYear(),this._currentDate.getMonth(),this._currentDate.getDate()-6),                   // start date for cached data.
		5:new Date(this._currentDate.getFullYear(),this._currentDate.getMonth()-1,this._currentDate.getDate()-2), // 5 minutes
		10: new Date(this._currentDate.getFullYear(),this._currentDate.getMonth()-3,this._currentDate.getDate()-10),// 10 minutes
		d:new Date(this._currentDate.getFullYear()-10,0,1),
		w:new Date(this._currentDate.getFullYear()-20,0,1),
		m:new Date(1970,0,1),
		lastTradeDate:new Date()		
	};
	this._numberOfCachedSymbol = 0;
	this._hasPopupSessionExpMsg = false;
	this._cacheMinuteIntradayData={}; // cache custom date and frequency intraday data.
	this._openDelayedPullDataRequest = false;
	this.sixTelekMap = {'238:10:IBOVESPA':true,'237:10:MERVAL':true,'390:10:CO-IGBC':true,'390:10:COLCAP':true,'398:10 E-ISBVL':true};// EUAM six telek map
	this.currencyForUKExchange = "GBX"; // GBX or GBP. the default value is "GBX"
};
RTQ_ChartDataManager.prototype = {
	openDelayedPullData:function(){  // request the delayed pull data to real-time draw chart.
	   var self = this;
	   this.pullInterval = setInterval(function(){
			var tickers=[],key,sixTelek = [];;
			for(var o in self.quoteDataMap){
				var q = self.quoteDataMap[o];
				if(q.dataType=="D"&&(q.type==1||q.type==10)){
				    key = q.exch+":"+q.type+":"+q.ticker;
					if(self.sixTelekMap&&self.sixTelekMap[key]){
						sixTelek.push(key);
					}else{
						tickers.push(key);
					}
				}
			}
			if(tickers.length){
				$.getJSON(self.dataUrls.pullDelayedData+"tickers="+tickers.join("}")+"&sixtelek=0&_t="+new Date().getTime()+"&cb=?",function(data){
					if(typeof(data)=="undefined"||data.errorcode)return;
					self.onPullDataArrived(data);
				});
			}
			if(sixTelek.length){  // request sixtelek index.
				$.getJSON(self.dataUrls.pullDelayedData+"tickers="+sixTelek.join("}")+"&sixtelek=1&_t="+new Date().getTime()+"&cb=?",function(data){
					if(typeof(data)=="undefined"||data.errorcode)return;
					self.onPullDataArrived(data);
				});
			}
		},15000);
	},
	onPullDataArrived:function(data){
        var list = data.items,gkey,q;
		if(typeof(list)=="undefined"||list ==null){
			return;
		}
        for(var i=0;i<list.length;i++){
            var exch = list[i].service;
            if (exch == "34"||exch == "17" || exch =="14" || exch == "19" || exch == "1") {                      // bats data 17.
                exch = "126";
            }
            var d = list[i].symbol.split("_");
            gkey = exch+":"+d[1];
			if(!gData||!gData[gkey]) continue;
            q= this.quoteDataMap[gData[gkey]["performanceId"]];
            if(!list[i].fields||!list[i].fields.length||!q)continue;
            var updateFields = {},tDate;
            for(var j=0;j<list[i].fields.length;j++){
                var obj = list[i].fields[j];
                if(obj.name=="Time"){
                    updateFields["Time"] = {value:parseInt(obj.value),type:"i"};
                }else if(obj.name=="LastPrice"){
                    updateFields["LastPrice"] = {value: parseFloat(obj.value),type:"f"};
                }else if(obj.name=="Volume"){
                    updateFields["Volume"] = {value:parseFloat(obj.value),type:"i"};
                }else if(obj.name=="Date"){
                    tDate = RTQ_ChartUtil.convertToDate(obj.value);
                }
            }
            var ld = q.lastTradeDate;
            if(RTQ_ChartUtil.compareDateByday(tDate,ld)||(typeof(ld)!="undefined"&& typeof(tDate)!="undefined"&&tDate.getTime()>ld.getTime())){  // the trade date is equal to last trade date.
                this.updateData(gkey,updateFields);
            }
        }
    },
	stopPull:function(){
		if(this.pullInterval) clearInterval(this.pullInterval);
	},
	// register a tickers  to chart data manager.
	registerSymbol:function(chartObj,tickers){ // tickers: it is a ticker oject or ticker list.{} or [{},{}...]
		var self = this;
		var tickerList = typeof(tickers)=="object"&&tickers.length>=0?tickers:[tickers];
		var registList = [];
		for(var i=0;i<tickerList.length;i++){
			var tickerObj = tickerList[i];
			if(!tickerObj.valid&&(tickerObj.type==1||tickerObj.type==10)&&(!tickerObj.performanceId||!tickerObj.tenforeTicker||!tickerObj.tenforeCode))continue;
			var keyId = tickerObj.performanceId;
			if(tickerObj.type == 8)keyId= tickerObj.secId; //fund
			if(!this.quoteDataMap[keyId]){
				this.quoteDataMap[keyId] = new quoteData(tickerObj.performanceId);
				this.quoteDataMap[keyId].ticker = tickerObj.tenforeTicker||tickerObj.ticker;
				this.quoteDataMap[keyId].mTicker = tickerObj.ticker;
				this.quoteDataMap[keyId].clientTicker = tickerObj.clientTicker;
				this.quoteDataMap[keyId].subExch = tickerObj.tenforeCode;  // for USA indexs.
				this.quoteDataMap[keyId].exch = tickerObj.gkey?tickerObj.gkey.split(":")[0]:tickerObj.tenforeCode;
				this.quoteDataMap[keyId].type = tickerObj.type; 
				this.quoteDataMap[keyId].country = tickerObj.country; 
				this.quoteDataMap[keyId].mType = tickerObj.mType; 
				this.quoteDataMap[keyId].performanceId = tickerObj.performanceId; 
				this.quoteDataMap[keyId].secId = tickerObj.secId; 
				if(tickerObj.gkey)this.quoteDataMap[keyId].gkey = tickerObj.gkey;
				if(tickerObj.type == 1||tickerObj.type==10||tickerObj.type==2)this.quoteDataMap[keyId].dataType = this._getIntradayType(this.quoteDataMap[tickerObj.performanceId].exch,tickerObj.type,this.quoteDataMap[keyId].ticker);
				else this.quoteDataMap[keyId].dataType="D";
				if(tickerObj.type==8){  // fund
					this.quoteDataMap[keyId].InitialDate = tickerObj.InitialDate;
					this.quoteDataMap[keyId].IMA_RDate = tickerObj.IMA_RDate;
					this.quoteDataMap[keyId].MStar_RDate = tickerObj.MStar_RDate;
				}
				this.registeredSymbols[keyId] = [chartObj];
				this.gkeyPIdMap[tickerObj.gkey]=keyId;  // create a map between gkey and key.
				registList.push(tickerObj);
				this._numberOfCachedSymbol++;
				if((tickerObj.type == 1||tickerObj.type==10||tickerObj.type==2)&&this.option.needUpdateIntradyData){  // need update intraday data from push proxy.
					setTimeout(function(){
						self.getIntradayData([{symbol:tickerObj.performanceId,days:5,datapoints:{p:"price",v:true}}],1,null,null,null);
					},1000);
				}
			}else{ 
				var charts = this.registeredSymbols[keyId],flag = false;
				for(var i=0;i<charts.length;i++){
					if(charts[i].id==chartObj.id) {  // has regested.
						flag = true;
						break;
					}
				}
				if(!flag){  // not in the registedSymbols.
					this.registeredSymbols[keyId].push(chartObj);
				}
			}
		}
		if(typeof(QSAPI.DataManager)=="object"&&registList.length){  // subscribe the ticker obj to push/pull.
			var methodName = this.dataSourceType=="push"?"subscribePush":"subscribePull";
			QSAPI.DataManager[methodName](this,registList,{});
		}	
	},
	unregisterSymbol:function(chartObj,tickers){  // unregister one ticker from chart data manager.
		var tickerList = typeof(tickers)=="object"&&tickers.length>=0?tickers:[tickers];
		unregistList = [];
		for(var i=0;i<tickerList.length;i++){
			var tickerObj = tickerList[i];
			if(typeof(tickerObj.vaild)!="undefined"&&!tickerObj.valid) continue;
			var symbol = tickerObj.type==8?tickerObj.secId: tickerObj.performanceId;
			var charts = this.registeredSymbols[symbol];
			if(charts&&charts.length){
				for(var j=0;j<charts.length;j++){
					if(charts[j].id==chartObj.id){
						charts.splice(j,1);
						if(charts.length <1){
							delete this.gkeyPIdMap[tickerObj.gkey];
							unregistList.push(tickerObj);
						}
						if(this._numberOfCachedSymbol>this.option.maximumOfCachedSymblos){// cache rearch to maximum.
							for(var o in this.quoteDataMap){
								if(this.registeredSymbols[symbol].length <1){  // 
									delete this.quoteDataMap[symbol];
									delete this.registeredSymbols[symbol];
									this._numberOfCachedSymbol--;
								}
							}
						}
					}	
				}									
			}
		}

		if(typeof(QSAPI.DataManager)=="object"&&unregistList.length){  // unsubscribe the ticker obj to push/pull.
			var methodName = this.dataSourceType=="push"?"unSubscribePush":"unSubscribePull";
			QSAPI.DataManager[methodName](this,unregistList,{});
		}
		
	},
	updateData: function(tickerObj,aData) {  // update the data from push/pull.
		var gKey = tickerObj.gkey;
		var time=null,symbol,d=null,symbol = this.gkeyPIdMap[gKey];
		if(this.quoteDataMap[symbol]){  // there is
			d = this.quoteDataMap[symbol].intradayData[1];
			if(aData["Time"]){  // include "time" field in aData.
				time = Math.floor(aData["Time"].value/60); // convert to minute.
			}else{   // get the "time from cache"
				if(typeof(gData)=="object"&&gData[gKey]&&gData[gKey]["TradeTime"]){
					time = Math.floor(gData[gKey]["TradeTime"]/60);
				}else if(typeof(gData)=="object"&&gData[gKey]&&gData[gKey]["Time"]){
					time = Math.floor(gData[gKey]["Time"]/60);
				}
			}
		 }
         if(d==null) return;
         if (aData["LastPrice"] != null&&time!=null) {
             this._cacheIntradayDatapoint(aData["LastPrice"].value,time,d.cachedPriceData,d,symbol,"p");
         }
         if (aData["Volume"] != null&&time!=null ) {
             this._cacheIntradayDatapoint(aData["Volume"].value,time,d.cachedVolumData,d,symbol,"v");
         }
		 if(aData["TradeDate"]!=null){// update the lastest trade date.
		 	if(QSAPI.DataManager.Chart)QSAPI.DataManager.Chart._refreshChartLastTradeDate(symbol,RTQ_ChartUtil.convertToDate(aData["TradeDate"].value));
		 }       
    },
	_cacheIntradayDatapoint:function(val,time,cdata,d,symbol,ty) { 
		if(time<d.openTime||time>d.closeTime)return; 
		var requestData = this.crossDomain?$.getJSON:$.get;
		var urlSuffix = this.crossDomain?"&jsoncallback=?":"";
        if (cdata != null && cdata.length > 0) { //
            var ddp= cdata[cdata.length-1];
            if (ddp != null && ddp[1] >= d.openTime && ddp[1] != time) {
				var st = d.openTime;
				var index = time - st, preIndx = ddp[1]- st;
				if(d!=null&& d.data.length){
					if (typeof(d.isUp) == "undefined") {
						d.isUp = 0;
					}
					if(ty=="p"){
						d.isUp = preIndx<=0?0:(cdata[cdata.length-1][0]>d.data[0].price[preIndx-1][1]?1:0);
						d.data[0].price[preIndx]=[preIndx,cdata[cdata.length-1][0],d.highPriceByMinitue,d.lowPriceByMinitue,cdata[0][0],d.isUp,cdata[cdata.length-1][1]]; //CHLO
						d.cachedPriceData = [];
						d.lowPriceByMinitue = NaN;
						d.highPriceByMinitue = NaN;
						this._updateDailyTTM(symbol,"p",d.data[0].price[preIndx]);
						this._refreshChart(symbol,"p");
					}else if(ty=="v"){
						d.data[0].accVolume[preIndx]=[preIndx,this._fiterAccvolume(cdata),d.isUp,cdata[cdata.length-1][1]];
						d.cachedVolumData = [];
						this._updateDailyTTM(symbol,"v",d.data[0].accVolume[preIndx]);
						this._refreshChart(symbol,"v");
					}		
				}						
			}
        }
        if(ty=="p"){
			if(d.cachedPriceData==null)d.cachedPriceData = [];
			var lgg = d.cachedPriceData.length;
			if(lgg>0&&d.cachedPriceData[lgg-1][0]== val&&d.cachedPriceData[lgg-1][1]== time)return false;// avoid the pull cache same data.
			d.cachedPriceData.push([val,time]); 
			if(!d.lowPriceByMinitue||isNaN(d.lowPriceByMinitue)||d.lowPriceByMinitue > val){  // calculate low price by minitue.
				d.lowPriceByMinitue = val;
			}
			if(!d.highPriceByMinitue||isNaN(d.highPriceByMinitue)||d.highPriceByMinitue < val){// calculate high price by minitue.
				d.highPriceByMinitue = val;
			}
		}else if(ty=="v"){
			if(d.cachedVolumData==null)d.cachedVolumData = [];
			var lgg = d.cachedVolumData.length;
            if(lgg>0&&d.cachedVolumData[lgg-1][0]== val&&d.cachedVolumData[lgg-1][1]== time)return false;  // avoid the pull cache same data.
			d.cachedVolumData.push([val,time]);
		}      
    },
	_fiterAccvolume:function(data){  // find the maxume of acc volume in cache for each minute
		var temp = 0,j=0;
		for(var i=0; i<data.length;i++){
			if(data[i][0]>temp){
				j=i;
				temp=data[i][0]
			}
		}
		return data.length>0?data[j][0]:NaN;
	},
	_updateDailyTTM:function(symbol,type,data){// type: price or volume.
	    var q = this.quoteDataMap[symbol];
	    if(!q)return;
		var ttm = q.dailyData.ttm, da = q.dailyData;
		if(!ttm)return;
		var lg = da.priceData.length;
		var isGBP = q.type==1&&(q.exch==151||q.exch=="152")&& QSAPI.DataManager.Chart.currencyForUKExchange=="GBP"// London stocks and currency is GBP. convert GBX to GBP
		var cdx = isGBP?0.01:1;
		if(type=="p"){
			if (!isNaN(data[2]) && data[2]*cdx > ttm.price[1]) {
				ttm.price[1] = data[2]*cdx;
				if(da.ready)da.priceData[lg-1][2] = data[2];
			}
			if (!isNaN(data[3]) && data[3]*cdx < ttm.price[2]) {
				ttm.price[2] = data[3]*cdx;
				if(da.ready)da.priceData[lg-1][3] = data[3]*cdx;
			}
			if (!isNaN(data[1])) {
				ttm.price[0] = data[1]*cdx;
				if(da.ready)da.priceData[lg-1][1] = data[1]*cdx;
			}
		}else if(type=="v"){
			if (data[1] > 0) {
				ttm.vol = data[1];
				if(da.ready)da.volumeData[da.volumeData.length-1][1] = data[1]/1000000;
			}
		}
	},
	_refreshChart:function(symbol,type){
		var chartObjs = this.registeredSymbols[symbol];
		if(!chartObjs||!chartObjs.length) return;
		for(var i = 0;i<[i].length;i++){
			if(typeof(chartObjs[i])=="object"&&typeof(chartObjs[i].refreshIntradayChart)=="function"){
				chartObjs[i].refreshIntradayChart(type,symbol);
			}
		}
	},
	_isSameDay:function(sd,d){
		return (sd.getFullYear()==d.getFullYear()&&sd.getMonth()==d.getMonth()&&sd.getDate()==d.getDate());
	},
	_refreshChartLastTradeDate:function(symbol,ldate,sdate){ // ldate is lastest trade days. sdate is the first day of lastest five days.
	    var q = this.quoteDataMap[symbol];
		if(q&&ldate){
			if(!q.lastTradeDate ){// first set the date.
               q.lastTradeDate = ldate;
            }else if(!this._isSameDay(q.lastTradeDate,ldate)&&q.lastTradeDate.getTime()<ldate.getTime()){
			   q.lastTradeDate = ldate;
			}
		    if(!q.refreshing&&q.intradayData&&typeof(q.intradayData.lastTradeDate)!="undefined"&&typeof(q.lastTradeDate)!="undefined"&&
			  !this._isSameDay(q.lastTradeDate,q.intradayData.lastTradeDate)
			  && q.lastTradeDate.getTime()>q.intradayData.lastTradeDate.getTime()){  // refresh the chart data from MSS
			    var self = this,urlSuffix = this.crossDomain?"&jsoncallback=?":"";
				var url = this.dataDomain+this.dataDomain+this.dataUrls.intraday+'ty='+q.dataType+'&exch='+((q.type==10&&$.inArray(parseInt(q.subExch,10),this.subUSExchange)>-1)?q.subExch:q.exch)+'&ticker='+q.ticker+(q.exch=="126"&&q.type==10?("_"+q.subExch):"")+'&stype='+q.type+'&days=5&_='+new Date().getTime()+urlSuffix;
				q.refreshing = true;
				requestData(url,function(aData){
					var jdata = self.crossDomain?aData:$.evalJSON(aData);
					var data = jdata==null?null:jdata.data;
					if(self._checkData(data)){  // the return data is right.
						if($.inArray(parseInt(jdata.exchangeId,10),self.subUSExchange)>-1&&jdata.security==10) jdata.exchangeId = "126";
						var syl = (jdata.exchangeId==126&&jdata.security==10)?jdata.symbol.split("_")[0]:jdata.symbol;
						var quote = self._getQuoteDataByExchAndTicker(jdata.exchangeId,syl);
						if(quote != null){
							quote.saveIntradayData(data,1);
							self._refreshChart(symbol,"p");
							quote.refreshing=false;
						}
					}
					q.refreshing = false;   // prevent request muil-times.
				});
		    }
		}
		var chartObjs = this.registeredSymbols[symbol];
		if(!chartObjs||!chartObjs.length) return;
		for (var i = 0; i < chartObjs.length; i++) {
			if(chartObjs[i]&&typeof(chartObjs[i].refreshLastTradeDate)=="function"&&ldate!=null){
				chartObjs[i].refreshLastTradeDate(sdate,ldate,symbol);
			}
		}
	},
	_getIntradayType:function(exchCode,type,tTicker){  // dealy or real-time
		var ty="D";
        if(typeof(QSAPI.DataManager)!="undefined"&&typeof(typeof(QSAPI.DataManager.permissionChecker)!="undefined")&&QSAPI.DataManager.permissionChecker.getSubcodes(type,exchCode,tTicker,"R",false).length>0){
            ty="R";
        }
		return ty;
	},
	_checkDataReady:function(infoList,type){  // fields: check PostTax return or price
	    var postTaxSymbol=[],priceSymbol = [],earningSymbol=[],dp,syl,ready=false;
        for(var i=0;i<infoList.length;i++){
            dp = infoList[i].datapoints, syl=infoList[i].symbol;
            if((dp["p"]||dp["v"]||dp["isD"]||dp["isS"])&&(!this.quoteDataMap[syl]||!this.quoteDataMap[syl].getDataByType(type).ready)){// get Price
                priceSymbol.push(syl);
            } 
            if((dp["postTax"])&&(!this.quoteDataMap[syl]||!this.quoteDataMap[syl].getDataByType(type).pTaxReady)){// get post Tax.
                postTaxSymbol.push(syl);
            }
            if(dp["e"]&&(!this.quoteDataMap[syl]||!this.quoteDataMap[syl].earningsData.ready)){
                earningSymbol.push(syl);
            } 
        }
        ready = !(priceSymbol.length||postTaxSymbol.length||earningSymbol.length);
        return {ready:ready,pSymbols:priceSymbol,pTaxSymbols:postTaxSymbol,eSymbols:earningSymbol};
	},
	_checkDividendDataReady:function(symbols){
		var flag = true;
		for (var i = 0; i < symbols.length; i++) {
			if (!this.quoteDataMap[symbols[i]] || !this.quoteDataMap[symbols[i]].dividendAndSplitData.ready) {
				flag = false;
			}
		}
		return flag;
	},
	_checkFundamentDataReady:function(symbol,fields,type){
		var rD = {ready:true,fields:[]},q,d;
		for(var i = 0; i < fields.length; i++){
			q = this.quoteDataMap[symbol],d = q.getDataByType(type);
			if(!q||!d||!d.fundamentalData[fields[i]]||!d.fundamentalData[fields[i]].length){
				rD.ready = false;
				rD.fields.push(this._fundamentCodeMap[fields[i]]);
			}
		}
		return rD;	
	},
	_getQuoteDataByExchAndTicker:function(exch,ticker){
		for(var o in this.quoteDataMap){
			if(this.quoteDataMap[o].exch == exch && this.quoteDataMap[o].ticker == ticker){
				return this.quoteDataMap[o];
			}
		}
		return null;
	},
	//infoList:[{symbol:"OP00002RH",days:5,type:"price",
	//datapoints:{p:true,v:true},indicators:{SMA:[],EMA:[]}]
	_getIntradayDataBySymbols:function(infoList,freq,sFreq,sIndex,eIndex){
		var rData = [],q;
		for(var j = 0; j<infoList.length;j++){
			q= this.quoteDataMap[infoList[j].symbol];
			rData[j]= {symbol:infoList[j].symbol};
			if(q!=null){
				var dp = infoList[j].datapoints;
				for(var o in dp){
					if((o=="p"||o=="postTax")&& dp[o]){
                        if(o=="postTax"){
                            rData[j].price = {data:[],pos:null};
                        }else{
                            rData[j].price = q.intradayData[sFreq].getPriceData(infoList[j].datapoints[o],infoList[j].days,sIndex,eIndex,freq);
                            if(rData[j].price!=null)rData[j].price.lastTradeDate = q.lastTradeDate;  // pass the last trade date to QS chart.
                        }
					}else if(o=="v"&& dp[o]){
						rData[j].volume = q.intradayData[sFreq].getVolumeData(infoList[j].days,sIndex,eIndex,freq);
					}else if(o=="indicators"){
						var inds = infoList[j].datapoints[o];
						rData[j].indicators = {};
						var oo,paras,vtype;
						for(var k=0;k<inds.length;k++){
							oo = inds[k].name,paras= inds[k].p, vtype = inds[k].vtype;
							if(oo=="EMA"||oo=="SMA"){
								if(!rData[j].indicators[oo]){
									rData[j].indicators[oo] = {};
								}
								rData[j].indicators[oo][paras[0]] = q.getIndicatorDataSegment(oo,sFreq,paras,sIndex,eIndex,infoList[j].days,vtype,rData[j].price,freq);
							}else if(oo=="VBP"){ // only cal
								var pD ,vD;
								if(!rData[j].price){
									pD = q.intradayData[sFreq].getPriceData(infoList[j].datapoints[o],infoList[j].days,sIndex,eIndex,freq).data;
								}else{
									pD = rData[j].price.data;
								}
								if(!rData[j].volume){
									vD = q.intradayData[sFreq].getVolumeData(infoList[j].days,sIndex,eIndex,freq).data;
								}else{
									vD = rData[j].volume.data;
								}
								rData[j].indicators[oo] = {data: techIndicatorCal.calculateVolByPrice(pD, vD, freq, 1),spos: null};
							}else{
								rData[j].indicators[oo]= q.getIndicatorDataSegment(oo,sFreq,paras,sIndex,eIndex,infoList[j].days,vtype,rData[j].price,freq);
							}						
						}
					}
				}			
			}	
		}
		return rData;
	},
	_getHistoricalDataBySymbols:function(infoList,freq,sIndex,eIndex){
		var rData = [],q,d,startIndex = sIndex,endIndex = eIndex;
		for(var i = 0;i<infoList.length;i++){
			rData[i] = {symbol:infoList[i].symbol};
			var dx = infoList[i].dx?infoList[i].dx:1;
			q = this.quoteDataMap[infoList[i].symbol];
			if(q == null)continue;
			d = q.getDataByType(freq);
			if(d == null)continue;
			for(var o in infoList[i].datapoints){
				if(o=="p"||o=="postTax"){ // price data or value data.
					if (i == 0) { // subject ticker.
						rData[i].price = d.getDataSegment(startIndex, endIndex, false, o, dx, infoList[i].datapoints[o]);
						if (rData[i].price.data != null&&rData[i].price.data.length>0) {
							startIndex = rData[i].price.data[0][rData[i].price.data[0].length-1];
							endIndex = rData[i].price.data[rData[i].price.data.length-1][rData[i].price.data[rData[i].price.data.length-1].length-1];
						}
					}else{
						rData[i].price = d.getDataSegment(startIndex, endIndex, true, o, dx, infoList[i].datapoints[o],rData[0].price.data);
					}
				}else if(o=="v"){// volume data
					rData[i].volume = d.getDataSegment(startIndex,endIndex,true,"v",dx,"price");// price indicate not covert to return.
				}else if(o=="e"){// earnings data
					rData[i].earningsData = q.earningsData.getDataSegment(startIndex, endIndex, false, "p", dx,"price");
				}else if(o=="ds"){// dividend and split
					var isD = infoList[i].datapoints[o].isD, isS = infoList[i].datapoints[o].isS;
					rData[i].dividendAndSplit = q.dividendAndSplitData.getDividendAndSplitDataSegment(startIndex,endIndex,"p",isD,isS); 
				}else if(o=="indicators"){// indicator data.
					var inds = infoList[i].datapoints[o];
					rData[i].indicators = {};
					var oo,paras, vtype;
					for(var k=0;k<inds.length;k++){
						oo = inds[k].name,paras= inds[k].p, vtype = inds[k].vtype;
						if (oo == "EMA" || oo == "SMA") {
							if (!rData[i].indicators[oo]) {
								rData[i].indicators[oo] = {};
							}
							rData[i].indicators[oo][paras[0]] = q.getIndicatorDataSegment(oo, freq, paras, startIndex, endIndex,null,vtype);
						}else if(oo == "RDividend"){
							rData[i].indicators[oo] = q.getRollDividend(freq,startIndex,endIndex);
						}else if(oo=="VBP"){ // only cal
							var pD ,vD;
							if(!rData[i].price){
								pD = d.getDataSegment(startIndex, endIndex, false, "p", dx, infoList[i].datapoints[o]).data;
							}else{
								pD = rData[i].price.data;
							}
							if(!rData[i].volume){
								vD = d.getDataSegment(startIndex,endIndex,true,"v",dx,"price").data;
							}else{
								vD = rData[i].volume.data;
							}
							rData[i].indicators[oo] = {data: techIndicatorCal.calculateVolByPrice(pD, vD, freq, 1),spos: null};
						}else{
							rData[i].indicators[oo] = q.getIndicatorDataSegment(oo, freq, paras, startIndex, endIndex,null,vtype);
						}
					}
				}
			}
		}
		return rData;
	},
	getChartNoteData:function(startDate,endDate,ticker,callback){
		if(typeof(callback)!="undefined"){
			DBHelper.loadNotesByDate(ticker,startDate,endDate,function(data){
				callback($.evalJSON(data));
			})
		}
	},
	_getFundamentalDataBySymbols:function(info,freq,startIndex,endIndex){
		var rData = {symbol:info.symbol},q,fields = info.fields;
		q = this.quoteDataMap[info.symbol];
		if(q == null)return rD;
		d = q.getDataByType(freq);
		if(d == null)return rD;
		for(var i = 0;i < fields.length; i++){
			if(d.fundamentalData[fields[i]]){
				var dType = fields[i]=="MReturnIndex"?"return":"price";
				rData[fields[i]] = d.getDataSegment(startIndex,endIndex,true,fields[i],1,dType); // not calculate return value.
			}
		}
		return rData;
	},
	_checkData:function(data){
		if (data == "-2") {
		  if (push && push.proxy && Util && Util.confirm && !this._hasPopupSessionExpMsg) {
		  	Util.confirm(TEXT.WSSaveTimeout, function(b){
		  		this._hasPopupSessionExpMsg = true;
		  		if (b) {
		  			window.location = 'logout.jsp?tmpid=' + QS_UserTID; // login again.
		  		}
		  	}, {
		  		title: 'Timeout'
		  	});
		  }
          return false;                        // sessionID is Invalid.
        } /*else if (data == "-4") {
           return false;                    //  no permission to access real-time data.
        } else if (data == "-1") {  // error.
           return false;
        }*/ else if(data ==null || !data.length ||data.length < 1) {
           return false;   // no intraday data.
        } else {
			return true;
		}
	},
	_getIntraydaySourceFrequency:function(stIndex,endIndex,freq,days){  // 
		var st = stIndex==null||isNaN(stIndex)?null:RTQ_ChartUtil.toDateFromIndex(stIndex);
		var ed = endIndex==null||isNaN(endIndex)?null:RTQ_ChartUtil.toDateFromIndex(endIndex);
		var mDate= this.dates[freq], f=NaN;
		if(days<=5||(st>=this.dates[1])||st==null){  // between five days.
			f = 1;
		}else if(st>=this.dates[5]&&(freq==5||freq==15)){
			f = 5;
		}else if(st>=this.dates[10]&&freq>=10){
			f=10;
		}else{               // need customize date.
			f = NaN;
		}
		return f;
	},
	_processMinutelyIntradayData:function(infoList,freq,list,self){
		var data=[];
		if(list&&list.length){  // has data.
			for(var i=0; i<list.length;i++){
				data[i]={symbol:infoList[i].symbol,price:{data:[],spos:null}};
				if(i==0)data[i].volume = {data:[],spos:null};
				var pdata,vdata,date,interVal,dty = infoList[i].datapoints.p?infoList[i].datapoints.p:"price",spos=null;
				var tyl = dty.split("_");  //return, return4,ten K growth.
				var ty = tyl[0];
				var isTenK = tyl.length>1;
				var index = 0,ot=570;
				for(var j=list[i].data.length-1;j>=0;j--){  //i=0 OHLC data.
					var d=list[i].data[j];
					pdata = d.security=="20"?d.bidPrice:d.lastPrice;//
					vdata = d.volume;
					date= RTQ_ChartUtil.convertToDate(d.date);
					spos= d.yesterdayClosePrice;
					ot = d.startTime;
					interVal = d.timeInterval;
					var tick, st=date.getTime()+ot*60000,intval=interVal*60000,isup=0,temp=0,vTemp=0;
					var pre = isTenK?10000:100,ad=isTenK?10000:0;
					for(var k=0;k<pdata.length;k++){
						if(spos == null || isNaN(spos) || spos <= 0){
							if(!isNaN(pdata[k][1]))spos=pdata[k][1];	
						}//fix no yest close price bug;
						tick = st+k*intval;
						isup=pdata[k][1]>temp?1:0;
						if(!isNaN(pdata[k][1])) temp=pdata[k][1];
						var dp = [index];
						if ((ty=="return" ||ty=="return4")&& spos>0){
							dp.push(ad+pre*(pdata[k][0]-spos)/spos);
							if(ty=="return4"){
								dp.push(ad+pre*(pdata[k][1]-spos)/spos);
								dp.push(ad+pre*(pdata[k][2]-spos)/spos);
								dp.push(ad+pre*(pdata[k][3]-spos)/spos);
							}else{
								dp.push(pdata[k][1]);
								dp.push(pdata[k][2]);
								dp.push(pdata[k][3]);
							}
						} else{
							dp = dp.concat(pdata[k]);
						}
						dp.push(isup);
						if(i==0){
							data[i].volume.data.push([index,(vdata[k]-vTemp)/1000000,isup,tick]); // volume.
							if(!isNaN(vdata[k])&&vdata[k]>0)vTemp = vdata[k];	
						}
						dp.push(tick);
						data[i].price.data.push(dp);				
						index++;
					}
					if(ty=="return" ||ty=="return4")data[i].price.spos=spos;
				}					
			}
		}
		if(infoList[0].datapoints.indicators && infoList[0].datapoints.indicators.length){// indicator data.
			var inds = infoList[0].datapoints.indicators;
			data[0].indicators = {};
			var oo,paras, vtype,q = self.quoteDataMap[infoList[0].symbol];
			var pData=data[0].price.data, vData=data[0].volume.data, dData=[];
			for(var k=0;k<inds.length;k++){
				oo = inds[k].name,paras= inds[k].p, vtype = inds[k].vtype;
				if (oo == "EMA" || oo == "SMA") {
					if (!data[0].indicators[oo]) {
						data[0].indicators[oo] = {};
					}
					data[0].indicators[oo][paras[0]] ={
						data:q._calculateIndicator(oo,pData,vData,dData,"i",paras)[0],
						spos:data[0].price.spos
					} 
				}else if(oo == "RDividend"){
					data[0].indicators[oo] = {data:[],spos:null};
				}else{
					var d = q._calculateIndicator(oo, pData, vData, dData, "i", paras);
					var dd=[];
					if(d&&d.length>1){
						for(var h=0;h<d.length;h++){
							dd.push({data:d[h],spos:data[0].price.spos});
						}
					}else if(d&&d.length==1){ // only one line
						dd = {data:d[0],spos:data[0].price.spos};
					}
					data[0].indicators[oo] = dd;
				}
			}
		}
		if(typeof(infoList[0].datapoints.p)=="undefined")delete data[0].price;
        if(typeof(infoList[0].datapoints.postTax)!="undefined") data[0].price={data:[],pos:null};
		if(typeof(infoList[0].datapoints.v)=="undefined")delete data[0].volume;
		return data;
	},
	//infoList:[{symbol:"OP00002RH",datapoints:{p:true,v:true,ds:{isD:true,isS:true},indicators:{SMA:[],EMA:[]}]
	//freq: 1,5,10,15,30,60 minites
	_getHistoricalMintelyData:function(infoList,freq,startIndex,endIndex,successCallback){
		var tickers=[],dType="D";
		var requestData = this.crossDomain?$.getJSON:$.get;
		var urlSuffix = this.crossDomain?"&jsoncallback=?":"";
		for(var i=0;i<infoList.length;i++){
			var qd = this.quoteDataMap[infoList[i].symbol];
			if(qd!=null)tickers.push((((qd.type==10&&$.inArray(parseInt(qd.subExch,10),this.subUSExchange)>-1)?qd.subExch:qd.exch)+":"+qd.ticker+(qd.exch=="126"&&qd.type==10?("_"+qd.subExch):"")+":"+qd.type+":"+qd.dataType));
			if(i==0&&qd!=null)dType=qd.dataType||'D';
		}
		var st = RTQ_ChartUtil.formatDate(RTQ_ChartUtil.toDateFromIndex(startIndex)) ;
		var ed = RTQ_ChartUtil.formatDate(RTQ_ChartUtil.toDateFromIndex(endIndex));
		var cacheKey= startIndex+"_"+endIndex+"_"+freq+"_"+tickers.join("|");
		var mainKey = startIndex+"_"+endIndex+"_"+freq+"_"+tickers[0];
		if(this._cacheMinuteIntradayData.key==cacheKey||(this._cacheMinuteIntradayData.mainkey==mainKey&&infoList.length==0)){ // get data from cache.
			var data;
			if(infoList.length==0){
				data = this._processMinutelyIntradayData(infoList,freq,[this._cacheMinuteIntradayData.data[0]],this);
			}else{
				data = this._processMinutelyIntradayData(infoList,freq,this._cacheMinuteIntradayData.data,this);
			}
			if(typeof(successCallback)=="function"){  // excute success callback.
					successCallback(data);
			}
		} else {
			if(tickers.length){
				var self = this;
				var url = this.dataDomain+this.dataUrls.minutelyIntraday+"tickers="+tickers.join("|")+"&st="+st+"&ed="+ed+"&f="+freq+"&ty="+dType+urlSuffix;
				requestData(url,function(aData){   // request data.
					var jData = self.crossDomain?aData:$.evalJSON(aData);
					self._cacheMinuteIntradayData.data = jData.tickers;
					self._cacheMinuteIntradayData.key=cacheKey;
					self._cacheMinuteIntradayData.mainkey=mainKey;
					var data = self._processMinutelyIntradayData(infoList,freq,jData.tickers,self);
					if(typeof(successCallback)=="function"){  // excute success callback.
							successCallback(data);
					}
				});
			}else{ // no tickers.
				if(typeof(successCallback)=="function"){  // excute success callback.
					successCallback({});
				}
			}	
		}
		
	},
	//infoList:[{symbol:"OP00002RH",datapoints:{p:true,v:true,ds:{isD:true,isS:true},indicators:{SMA:[],EMA:[]}]
	//freq: "d","w","m"
	getHistoricalData:function(infoList,freq,startIndex,endIndex,successCallback){
		var requestData = this.crossDomain?$.getJSON:$.get;
		var urlSuffix = this.crossDomain?"&jsoncallback=?":"";
		if(freq=="d"||freq=="w"||freq=="m"){  // get historical data from TB.
			var self = this;
			var rs = this._checkDataReady(infoList,freq);
			if(rs.ready){
				var rData = self._getHistoricalDataBySymbols(infoList,freq,startIndex,endIndex);
				if(typeof(successCallback)=="function"){  // excute success callback.
					successCallback(rData);
				}
			} else {
				if(rs.pSymbols.length>0){  // need to get the price data.
					var stocks =[],funds=[],forexs=[];
					for(var h=0;h<rs.pSymbols.length;h++){
						var qm = this.quoteDataMap[rs.pSymbols[h]];
                        if(qm.mType=="FE"&&qm.type==10||qm.type==8){  // etf nav and fund.
                            funds.push(qm.secId+(qm.country?("_"+qm.country):""));
                        }else if(qm.type==20){  // forex
                            forexs.push(rs.pSymbols[h]);
                        }else{   // stock, close fund, etf ticker.
                            stocks.push(rs.pSymbols[h]+(qm.country?("_"+qm.country):""));
                        }
					}
					var st = RTQ_ChartUtil.formatDate(this.dates[freq]) , ed = RTQ_ChartUtil.formatDate(this.dates["lastTradeDate"]);
					if(stocks.length){ // exist stock ,etf, close fund, index market price.
						var tickers = stocks.join(",");
						var isDividendReady = this._checkDividendDataReady(rs.pSymbols);
						var needGetDS = isDividendReady?0:1;
						var url = this.dataDomain+this.dataUrls.historical+'symbols='+tickers+'&f='+freq+'&isV=1&isD='+needGetDS+'&isS='+needGetDS+'&st='+st+'&ed='+ed+urlSuffix;
						requestData(url,function(aData){
							var jdata = self.crossDomain?aData:$.evalJSON(aData);
							var data = jdata==null?null:jdata.Data; //
					        if(self._checkData(data)){  // the return data is right.
					        	var s,quote;
					        	for(var i = 0;i < data.length; i++){
									s = data[i].Symbol;
									quote = self.quoteDataMap[s];
									if(quote != null){
										quote.saveTimeSeriesData(data[i],freq);  				
									}
								}
								var rss = self._checkDataReady(infoList,freq);
								if(rss.ready){  // data is ready now
									var rData = self._getHistoricalDataBySymbols(infoList,freq,startIndex,endIndex);
									if(typeof(successCallback)=="function"){  // excute success callback.
										successCallback(rData);
									}
								}   //  not ready
					        }
						});
					}
					if(funds.length){  // request etf NAV values or Fund NAV.
						var eTickers = funds.join(",");
						var url = this.dataDomain+this.dataUrls.fundHistorical+'symbols='+eTickers+'&f='+freq+'&st='+st+'&ed='+ed+'&dp=1,1578'+urlSuffix;
						requestData(url,function(aData){
                            var jdata = self.crossDomain?aData:$.evalJSON(aData);
                            var data = jdata==null?null:jdata.Data; //
                            if(self._checkData(data)){  // the return data is right.
                                var s,quote;
                                for(var i = 0;i < data.length; i++){
                                    s = data[i].Symbol;     // for etf IIV ticker s is performance id.for mutural fund s is SecId.
									if(self.quoteDataMap[s]){  
										quote = self.quoteDataMap[s];
									}else{
										for(var oo in self.quoteDataMap){
	                                        if(self.quoteDataMap[oo].secId == s){
	                                            quote = self.quoteDataMap[oo];
	                                        }
	                                    }
									}								
                                    //quote = self.quoteDataMap[s];
                                    if(quote != null){
                                        quote.saveFundData(data[i],freq);                       
                                    }
                                }
                                var rss = self._checkDataReady(infoList,freq);
                                if(rss.ready){  // data is ready now
                                    var rData = self._getHistoricalDataBySymbols(infoList,freq,startIndex,endIndex);
                                    if(typeof(successCallback)=="function"){  // excute success callback.
                                        successCallback(rData);
                                    }
                                }   //  not ready
                            }
                        });
					}
                    if(forexs.length>0){  // get Forex historical data from TSDB
                        var tickers = forexs.join(",");
                        var url = this.dataDomain+this.dataUrls.forex+'symbols='+tickers+'&f='+freq+'&type=2121&st='+st+'&ed='+ed+urlSuffix;
                        requestData(url,function(aData){
                            var jdata = self.crossDomain?aData:$.evalJSON(aData);
                            var data = jdata==null?null:jdata.Data; //
                            if(self._checkData(data)){  // the return data is right.
                                var s,quote;
                                for(var i = 0;i < data.length; i++){
                                    s = data[i].Symbol;
                                    quote = self.quoteDataMap[s];
                                    if(quote != null){
                                        quote.saveTimeSeriesData(data[i],freq,20);                 
                                    }
                                }
                                var rss = self._checkDataReady(infoList,freq);
                                if(rss.ready){  // data is ready now
                                    var rData = self._getHistoricalDataBySymbols(infoList,freq,startIndex,endIndex);
                                    if(typeof(successCallback)=="function"){  // excute success callback.
                                        successCallback(rData);
                                    }
                                }   //  not ready
                            }
                        });     
                   
                    }
				}
				if(rs.eSymbols.length>0){ // need to get the earning data.
					var stt = RTQ_ChartUtil.formatDate(this.dates["m"]),edd = RTQ_ChartUtil.formatDate(this.dates["lastTradeDate"]);
					var fieldCodes = this._fundamentCodeMap["Earnings"];
					var qd = this.quoteDataMap[rs.eSymbols[0]];
					var country = qd!=null?qd.country:null;
					var url = this.dataDomain+this.dataUrls.fundamental+'symbols='+rs.eSymbols[0]+'&fields='+fieldCodes+'&f=d&st='+stt+'&ed='+edd+'&country='+country+urlSuffix;
					requestData(url,function(aData){
						var jdata = self.crossDomain?aData:$.evalJSON(aData);
						var data = jdata==null?null:jdata.data;
				        if(self._checkData(data)||data==null){  //null indicate no earning data. save the earning data     	
							if (data == null) {
								var qq = self.quoteDataMap[rs.eSymbols[0]];
								if(qq) qq.earningsData.ready = true;
							}
							if(data!=null && data.length){		
								var s = data[0].symbol, d = data[0].field;		
								var quote = self.quoteDataMap[s];
								quote.saveEarningsData(d[0]);
							}				
							var rss = self._checkDataReady(infoList,freq);
							if(rss.ready){  // data is ready now
								var rData = self._getHistoricalDataBySymbols(infoList,freq,startIndex,endIndex);
								if(typeof(successCallback)=="function"){  // excute success callback.
									successCallback(rData);
								}
							} //  not ready
				        }
					});
				}
                if(rs.pTaxSymbols.length>0){  // need to request the pos-Tax return
                    var st = RTQ_ChartUtil.formatDate(this.dates[freq]) , ed = RTQ_ChartUtil.formatDate(this.dates["lastTradeDate"]);
                    var pts = [];
                    for(var i =0;i<rs.pTaxSymbols.length;i++){
                        if(this.quoteDataMap[rs.pTaxSymbols[i]])pts.push(this.quoteDataMap[rs.pTaxSymbols[i]].secId)
                        else{pts.push(rs.pTaxSymbols)}
                    }
                    var pTaxs = pts.join(",");
                    var url = this.dataDomain+this.dataUrls.fundHistorical+'symbols='+pTaxs+'&f='+freq+'&st='+st+'&ed='+ed+'&dp=15&aj=1'+urlSuffix;
                    requestData(url,function(aData){
                        var jdata = self.crossDomain?aData:$.evalJSON(aData);
                        var data = jdata==null?null:jdata.Data; //
                        if(self._checkData(data)){  // the return data is right.
                            var s,quote;
                            for(var i = 0;i < data.length; i++){
                                s = data[i].Symbol;     // for etf IIV ticker s is performance id.for mutural fund s is SecId.
                                if(self.quoteDataMap[s]){    // fund 
                                    quote = self.quoteDataMap[s];
                                }else{
                                    for(var oo in self.quoteDataMap){
                                        if(self.quoteDataMap[oo].secId == s && self.quoteDataMap[oo].type != 10){ // etf
                                            quote = self.quoteDataMap[oo];
                                        }
                                    }
                                }                               
                                //quote = self.quoteDataMap[s];
                                if(quote != null){
                                    quote.saveFundData(data[i],freq);                       
                                }
                            }
                            var rss = self._checkDataReady(infoList,freq);
                            if(rss.ready){  // data is ready now
                                var rData = self._getHistoricalDataBySymbols(infoList,freq,startIndex,endIndex);
                                if(typeof(successCallback)=="function"){  // excute success callback.
                                    successCallback(rData);
                                }
                            }   //  not ready
                        }
                    });
                }
			}
		} else {                        // get more intraday data from MSS.
			this._getHistoricalMintelyData(infoList,freq,startIndex,endIndex,successCallback);
		}
		
	},
	//infoList:[{symbol:"OP00002RH",days:5,type:"price",datapoints:{p:true,v:true},indicators:{SMA:[],EMA:[]}]
	// freq:5,10,15,30,60.
	getIntradayData:function(infoList,freq,startIndex,endIndex,successCallback){ //type:return or price
		var requestData = this.crossDomain?$.getJSON:$.get;
		var urlSuffix = this.crossDomain?"&jsoncallback=?":"";
		var self = this, symbols = [],dType="D",secType="";
		for(var j=0;j<infoList.length;j++){
			symbols.push(infoList[j].symbol);
			if (j == 0) {
				var qd = this.quoteDataMap[infoList[j].symbol];
				if (qd && qd != null && typeof(qd.dataType) != "undefined") {
                    dType = qd.dataType;
                    secType = qd.type;
                }
			}
		}
		var days = infoList&&infoList.length?infoList[0].days:null;
		var sFreq = this._getIntraydaySourceFrequency(startIndex,endIndex,freq,days);
		if(isNaN(sFreq)){   // customize intraday data from MSS.
			this._getHistoricalMintelyData(infoList,freq,startIndex,endIndex,successCallback);
		}else{  // get intray data and cache the intrayday.
			var rs = this._checkDataReady(infoList,sFreq);  // only check price data.
			if(rs.pSymbols.length == 0){
				var rData = this._getIntradayDataBySymbols(infoList,freq,sFreq,startIndex,endIndex);
				if(typeof(successCallback)=="function"){  // excute success callback.
					successCallback(rData);
				}
			} else {
				if(sFreq==1&&secType!="20"){  // not include forex .
					for(var i=0;i<rs.pSymbols.length;i++){
						var qd = this.quoteDataMap[rs.pSymbols[i]];
						if(!qd)continue;
						if(qd.type==8){  // there is no intraday data for fund ticker.
							qd.intradayData[sFreq].ready = true;
							var rss = self._checkDataReady(infoList,sFreq);
                            if(rss.ready){  // data is ready now
                                var rData = self._getIntradayDataBySymbols(infoList,freq,sFreq,startIndex,endIndex);
                                if(typeof(successCallback)=="function"){  // excute success callback.
                                    successCallback(rData);
                                }
                            }
							continue;
						}
						var url = this.dataDomain+this.dataUrls.intraday+'ty='+qd.dataType+'&exch='+((qd.type==10&&$.inArray(parseInt(qd.subExch,10),this.subUSExchange)>-1)?qd.subExch:qd.exch)+'&ticker='+qd.ticker+(qd.exch=="126"&&qd.type==10?("_"+qd.subExch):"")+'&stype='+qd.type+'&days=5'+urlSuffix;
						requestData(url,function(aData){
							var jdata = self.crossDomain?aData:$.evalJSON(aData);
							var data = jdata==null?null:jdata.data;
					        if(self._checkData(data)){  // the return data is right
								if($.inArray(parseInt(jdata.exchangeId,10),self.subUSExchange)>-1&&jdata.security==10) jdata.exchangeId = "126";
								var syl = (jdata.exchangeId==126&&jdata.security==10)?jdata.symbol.split("_")[0]:jdata.symbol;
								var quote = self._getQuoteDataByExchAndTicker(jdata.exchangeId,syl);
								if(quote != null){
									quote.saveIntradayData(data,1);
									var rss = self._checkDataReady(infoList,sFreq);
									if(rss.pSymbols.length == 0){  // data is ready now
										var rData = self._getIntradayDataBySymbols(infoList,freq,sFreq,startIndex,endIndex);
										if(typeof(successCallback)=="function"){  // excute success callback.
											successCallback(rData);
										}
									}
								}
							}
						});
					}
				}else {  
					var st = RTQ_ChartUtil.formatDate(this.dates[sFreq]),ed = RTQ_ChartUtil.formatDate(this.dates["lastTradeDate"]);
					var tickers=[];
					for(var h=0;h<rs.pSymbols.length;h++){
						var qdd = this.quoteDataMap[rs.pSymbols[h]];
						if(qdd.type==8){  // there is no intraday data for mutual fund.
							qdd.intradayData[sFreq].ready = true;
                            continue;
						}
						if(qdd!=null)tickers.push((((qdd.type==10&&$.inArray(parseInt(qdd.subExch,10),this.subUSExchange)>-1)?qdd.subExch:qdd.exch)+":"+qdd.ticker+(qdd.exch=="126"&&qdd.type==10?("_"+qdd.subExch):"")+":"+qdd.type+":"+qdd.dataType));
					}
					if(tickers.length){  //there is tickers need to get intraday data.
						var url = this.dataDomain+this.dataUrls.minutelyIntraday+"tickers="+tickers.join("|")+"&st="+st+"&ed="+ed+"&f="+sFreq+"&ty="+dType+urlSuffix;
	                    requestData(url,function(aData){
	                        var jdata = self.crossDomain?aData:$.evalJSON(aData);
	                        var data = jdata==null?null:jdata.tickers;
	                        if(self._checkData(data)){  // the return data is right.
	                            for(var k=0;k<data.length;k++){
									if($.inArray(parseInt(data[k].exchangeId,10),self.subUSExchange)>-1&&data[k].security==10) data[k].exchangeId = "126";
	                                var syl = data[k].security==10?data[k].symbol.split("_")[0]:data[k].symbol;
									var quote = self._getQuoteDataByExchAndTicker(data[k].exchangeId,syl);
	                                if(quote != null){
	                                    quote.saveIntradayData(data[k].data,sFreq);
	                                    var rss = self._checkDataReady(infoList,sFreq);
	                                    if(rss.pSymbols.length == 0){  // data is ready now
	                                        var rData = self._getIntradayDataBySymbols(infoList,freq,sFreq,startIndex,endIndex);
	                                        if(typeof(successCallback)=="function"){  // excute success callback.
	                                            successCallback(rData);
	                                        }
	                                    }
	                                }
	                            }       
	                        }
	                    });
					}else{  // not need get intraday data.
						var rss = self._checkDataReady(infoList,sFreq);
	                    if(rss.pSymbols.length == 0){  // data is ready now
	                        var rData = self._getIntradayDataBySymbols(infoList,freq,sFreq,startIndex,endIndex);
	                        if(typeof(successCallback)=="function"){  // excute success callback.
	                            successCallback(rData);
	                        }
	                    }
					}
					
				}
				
			}
		}
		
	},
	//infoList:{symbol:"0P000002RH",fields:["PE","PS","PB","PC","FairValue"}
	getFundamentalData:function(infoList,freq,startIndex,endIndex,successCallback){
		var requestData = this.crossDomain?$.getJSON:$.get;
		var urlSuffix = this.crossDomain?"&jsoncallback=?":"";
		var self = this,symbol = infoList.symbol, fields = infoList.fields,fieldCodes;
		if(freq!="d"&&freq!="w"&&freq!="m"){  // if minutely data .
			if(typeof(successCallback)=="function"){  // excute success callback.
				var rd={};
				for(var i = 0;i < fields.length;i++){
					rd[fields[i]]={data:[],spos:null};
				}
				successCallback(rd);
			}
			return;
		}
		var rs = this._checkFundamentDataReady(symbol,fields,freq);
		if(rs.ready){
			var rData = self._getFundamentalDataBySymbols(infoList,freq,startIndex,endIndex);
			if(typeof(successCallback)=="function"){  // excute success callback.
				successCallback(rData);
			}
		} else {
			fieldCodes = rs.fields.join(",");
			var qd = this.quoteDataMap[symbol], country = qd!=null?qd.country:null;
			var st = RTQ_ChartUtil.formatDate(this.dates[freq]) , ed = RTQ_ChartUtil.formatDate(this.dates["lastTradeDate"]);
			var url = this.dataDomain+this.dataUrls.fundamental+'symbols='+symbol+'&fields='+fieldCodes+'&f='+freq+'&st='+st+'&ed='+ed+'&country='+country+urlSuffix;
			requestData(url,function(aData){  
				var jdata = self.crossDomain?aData:$.evalJSON(aData);
				var data = jdata==null?null:jdata.data;
		        if(self._checkData(data)){  // the return data is right.
		        	var s,quote,d,name;
		        	for(var i = 0;i < data.length; i++){
						s = data[i].symbol, d = data[i].field
						quote = self.quoteDataMap[s];
						if(quote != null){
							for(var j = 0;j < d.length;j++){
								name = self._fundamentNameMap[d[j].name];
								quote.saveFundamentalData(d[j],freq,name);
							}							
						}
					}
					var rss = self._checkFundamentDataReady(symbol,fields,freq);
					if(rss.ready){  // data is ready now
						var rData = self._getFundamentalDataBySymbols(infoList,freq,startIndex,endIndex);
						if(typeof(successCallback)=="function"){  // excute success callback.
							successCallback(rData);
						}
					}
		        }else{     // data not aviable.
					if(typeof(successCallback)=="function"){  // excute success callback.
						var rd={};
						for(var i = 0;i < fields.length;i++){
							rd[fields[i]]={data:[],spos:null};
						}
						successCallback(rd);
					}
				}
			});
		}
	},
    //symbol:"0P000002RH"
    getValuationData:function(symbol,freq,startIndex,endIndex,successCallback){
        if(freq!="d"&&freq!="w"&&freq!="m"){  // if minutely data .
            if(typeof(successCallback)=="function"){  // excute success callback.
                successCallback({
                    Valuation: {
                        data: [],
                        spos: null
                    }
                });
            }
            return;
        }
        var calData = function(f,p){
            var v = [],vay;
            if(f&&p&&f.length&p.length){
                for(var i=0;i<f.length;i++){
					vay = typeof(p[i])=="undefined"||typeof(f[i])=="undefined"?NaN:p[i][1]/f[i][1]
                    v.push([i,vay,f[i][2]]);
                }
            }
            return v;
        };
        var fData = null, pData = null;
        this.getFundamentalData({symbol:symbol,fields:["FairValue"]},freq,startIndex,endIndex,function(fd){ // get fairValue.
            fData = fd["FairValue"].data;  
            if(fData&&pData){
                var d = calData(fData,pData);
                if (typeof(successCallback) == "function") {
                    successCallback({data:d,spos:null});
                }
            }
        });
        this.getHistoricalData([{symbol:symbol,datapoints:{p:"price"}}],freq,startIndex,endIndex,function(pd){
            pData = pd[0]["price"].data;
            if(fData&&pData){
                var d = calData(fData,pData);
                if (typeof(successCallback) == "function") {
                    successCallback({data:d,spos:null});
                }
            }
        });
        
    },
	exportDataToExcel:function(symbols,type,st,ed,frequency,days){
		var url=this.dataDomain+this.dataUrls.exportDataToExcel;
		if(type == 1||type == 4 || type == 5){   // historical date.(stock, fund and forex)
			var tList = [];
			for (var i = 0; i < symbols.length; i++) {
				qd = this.quoteDataMap[symbols[i]];
				if(qd!=null){
					tList.push(qd.clientTicker||qd.mTicker);
				}
			}
			url+="symbols="+symbols.join(",")+"&st="+st+"&ed="+ed+"&f="+frequency+"&dty="+type+"&tickers="+ encodeURIComponent(tList.join(",").replace("/",""));
		}else if(type == 0 || type == 2 || type==3){ //last five or one intraday data.
			var qd = null, tickers = [];
			for(var i=0;i<symbols.length;i++){
				qd = this.quoteDataMap[symbols[i]];
				if(qd!=null){
					tickers.push(qd.exch+":"+qd.ticker+(qd.exch=="126"&&qd.type==10?("_"+qd.subExch):"")+":"+qd.type+":"+qd.dataType);
				}
			}
			if(type == 0){
				url+="symbols="+tickers.join("|")+"&days="+days+"&dty="+type;
			}else{
				url+="symbols="+tickers.join("|")+"&f="+frequency+"&st="+st+"&ed="+ed+"&dty="+type;
			}
		}
		/*else if(type == 4){ // fund historical.
			var tList = [],pathList=[];
			for (var i = 0; i < symbols.length; i++) {
				qd = this.quoteDataMap[symbols[i]];
				if(qd!=null){
					tList.push(qd.mTicker);
					pathList.push(this._buildFundDataUrl(symbols[i],frequency,st,ed));
				}
			}
			url += "symbols="+tList.join("|")+"&dty="+type+"&paths=" + pathList.join("[");
		}*/
		window.open(url);
	},
	_buildFundDataUrl:function(symbol,freq,sDate,eDate){
		var ids = "",startDate="",endDate="";
		var q = this.quoteDataMap[symbol];
		if(q==null||q.type != 8)return "";
		var d = new Date();
		endDate = typeof(sDate)!="undefined"?eDate:d.getFullYear()+"-"+(d.getMonth()+1)+"-"+d.getDate();
		if (freq == "d") {
			startDate = typeof(sDate)!="undefined"?sDate:(d.getFullYear() - 1) + "-" + (d.getMonth() + 1) + "-" + d.getDate();
		}else if(freq=="m"){
			startDate = typeof(sDate)!="undefined"?sDate:(d.getFullYear() - 20) + "-" + (d.getMonth() + 1) + "-" + d.getDate();
		}
		ids+=q.secId+':'+q.performanceId+':CU$$$$$USD'+':1:1:'+q.mType+':'+q.InitialDate+':'+q.IMA_RDate+':'+q.MStar_RDate;
		var url = 'http://globalquote.morningstar.com/globalcomponent/GetFundInteractiveData.ashx?chart=quote';
		url += '&w=955&h=266&lan=en-US&reg=USA&tc=CU$$$$$USD&p=true&d=true&r=false&win=3&period=custom&';
		url+='range='+startDate+'|'+endDate+'&cfg=scal:0|GF:7&ma=&ids='+ids+'&format=json';
		return encodeURIComponent(url);
	},
	setData: function(symbol, frequency, dataType, subType, data){ // dataType:events,intraday,historical. subType:event:DividendAndSplits,Earnings;
		if (typeof(this.quoteDataMap[symbol]) == "undefined") { // intraday: price, volmue; historcal:price,volmue,fundamental and valution data.
			this.quoteDataMap[symbol] = new quoteData(symbol);
		}
		var dataObj = null;
		if (dataType == "events") { // discrete data.
			if (subType == "DividendAndSplits") {
				dataObj = this.quoteDataMap[symbol].dividendAndSplitData;
			}else if (subType == "Earnings") {
				dataObj = this.quoteDataMap[symbol].earningsData;
			}else { // custom data.
				this.quoteDataMap[symbol][subType] = new timeSeriesData("d");
				dataObj = this.quoteDataMap[symbol][subType];
			}
			dataObj.priceData = data;
		}else if (dataType == "intraday") { // frequency is 1 minute, 5 minute and 10 minutes.
			dataObj = this.quoteDataMap[symbol].intradayData[frequency];
			dataObj.data = data;          // data fomate:{yestClosePrice:25.6,date:"2010-2-3",price:[[0,close,high,low,open,time].....],accVolume:[[0,volume,time]]}
			dataObj.frequency = frequency;
		}
		else if (dataType == "historical") { // frequecy is daily, weekly, monthly.
			if (frequency == "d") {     
				dataObj = this.quoteDataMap[symbol].dailyData;
			}
			else if (frequency == "w") {
				dataObj = this.quoteDataMap[symbol].weeklyData;
			}
			else if (frequency == "m") {
				dataObj = this.quoteDataMap[symbol].monthlyData;
			}
			dataObj.frequency = frequency;
			if (subType == "price") {      //data format:[[0,[0,close,high,low,open,flag,date]],[1,volume,flag,date]]].
				dataObj["priceData"] = data;
			}else if (subType == "volume") { //data format:[[0,[0,volume,flag,date]],[1,volume,flag,date]]].
				dataObj["volumeData"] = data;
			}else { // fundamental and valation
				dataObj["fundamentalData"][subType] = data;
			}
			dataObj.startIndex = data[0][data[0].length - 1];
			dataObj.endIndex = data[data.length - 1][data[data.length - 1].length - 1];
		}
		if (dataObj != null) {
			dataObj.symbol = symbol;
			dataObj.ready = true;
		}
	},
	removeData:function(symbol){  // remove all data for one symbol in cache.
	   if(typeof(this.quoteDataMap[symbol]) != "undefined"){  
	   		delete this.quoteDataMap[symbol];
	   }
	}
}
function intrayData(f) {
    this.data = [];// {accVolume:[],price:[],date:datetime,yestClosePrice:number}
    this.symbol; // performance ID
    this.exch; // tenforeCode
    this.ticker; // tenforeTicker
    this.openTime = 570;
    this.closeTime = 960;
    this.cachedPriceData = []; // cache last price in one minute.
    this.cachedVolumData = []; // cache accumulative volume in one minute.
    this.ready = false;
	this.frequency = f;
};

intrayData.prototype = {
	_getIndexs:function(sData,num,startIndex,endIndex){  // sData is order by DESC.
		if ((typeof(num) == "undefined" || num == null) && startIndex != null && endIndex != null) {
			var dIndex = 0, st = 0, end = sData.length - 1, flag = true;
			for (var i = 0; i < sData.length; i++) {
				dIndex = RTQ_ChartUtil.toDateIndex(sData[i].date);
				if (dIndex <= endIndex && flag) {
					end = i;
					flag = false;
				}
				if (startIndex <= dIndex) {
					st = i;
				}
				else {
					break;
				}
			}
		}else{
			st = num < 1?0:num > sData.length?(sData.length -1):(num - 1);
			end = 0;
		}
		return [st,end];
	},
    getPriceData: function(ty,num,stIndex,edIndex,fre) { // get the intraday datas. ty="return": return, ty="price": price.
    	if(this.data.length < 1) return {data:[],spos:null};
		//var sIndex = num < 1?0:num > this.data.length?(this.data.length -1):(num - 1);
        var sdata = [],spos = null;
		if(fre == 1440){  // filter to daily data (for slider chart requirment only)
			var j = 0;
			for(var i = this.data.length-1;i >=0 ;i--){
				sdata.push([j,this.data[i].price[this.data[i].price.length-1][1],RTQ_ChartUtil.toDateIndex(this.data[i].date)]);
				j++;
			}
		}else {
			var st=0, d,tick,lg;
			var tyl = ty.split("_");
			var ty = tyl[0];
			var isTenK = tyl.length>1;
			var f = Math.round(fre/this.frequency);  // interval
			var indexs = this._getIndexs(this.data,num,stIndex,edIndex);
			for(var i = indexs[0]; i >= indexs[1];i--){
				d = this.data[i];
				tick = d.date == null?0:d.date.getTime();
				lg = d.price.length;
				if(spos == null){
					spos = d.yestClosePrice;
				}
				var o,l,c,h, pre = isTenK?10000:100,ad=isTenK?10000:0;
				for(var j = 0;j < lg; j+=f){
					if(j>0&&f>0){   // calculate H L
						o = d.price[j-f+1][4], c = d.price[j][1], h = d.price[j-f+1][2],l = d.price[j-f+1][3];
						for(var s=j-f+2;s<=j;s++){
							if(h<d.price[s][2]){
								h = d.price[s][2];
							}
							if(l>d.price[s][3]){
								l = d.price[s][3];
							}		
						}
					}else if(j==0){
						o = d.price[j][4], c = d.price[j][1], h = d.price[j][2],l = d.price[j][3]; // first point.
					}	
					var dp = [];
					if((spos == null || isNaN(spos) || spos <= 0) && d.price[j]!=null && !isNaN(d.price[j][1]) && d.price[j][1]>0){
						spos=d.price[j][1];
					}//fix no yest close price bug;
					dp.push(j/f+st);
	                if ((ty=="return" ||ty=="return4")&& spos>0) { // convert price to return value.
	                    if (ty == "return") {
							dp.push(ad+pre * (c - spos) / spos);
							dp.push(h), dp.push(l), dp.push(o);
						}else{
							dp.push(ad+pre * (c - spos) / spos);
							dp.push(ad+pre * (h - spos) / spos), dp.push(ad+pre * (l- spos) / spos), dp.push(ad+pre * (o- spos) / spos);
						}
	                } else {
	                   dp.push(c),dp.push(h), dp.push(l), dp.push(o);
	                }
					dp.push(d.price[j][5]);
					dp.push(d.price[j][6]);// add the fill flag.
	                dp.push(tick+d.price[j][d.price[j].length-1]*60000);
	                sdata.push(dp);
				}
				st+= (Math.floor(lg/f)+(lg%f==0?0:1));
			}
		} 
        return {data:sdata,spos:spos};
    },
  
    getVolumeData:function(num,stIndex,edIndex,fre) {    // convert accumulative volume to standard volume
    	if(this.data.length < 1) return {data:[],spos:null};
		//var sIndex = num < 1?0:num > this.data.length?(this.data.length -1):(num - 1);
        var sdata = [],tdata= [];
        var st=0, d,tick,lg,temp=0;
		var f = Math.round(fre/this.frequency);  // interval
		var indexs = this._getIndexs(this.data,num,stIndex,edIndex);
        for (var i = indexs[0]; i >= indexs[1]; i--) {
			d = this.data[i];
            var tick = d.date == null?0:d.date.getTime();
			lg = d.accVolume.length;
			temp = 0;
            for (var j = 0;j < lg;j+=f) {
                sdata.push([j/f+st,d.accVolume[j][1]==0?NaN:(d.accVolume[j][1]-temp)/1000000,d.accVolume[j][2],tick + d.accVolume[j][3]*60000]);
                if (!isNaN(d.accVolume[j][1])) {
                    temp = d.accVolume[j][1];
                }
            }
            st+=(Math.floor(lg/f)+(lg%f==0?0:1));
        }
        return {data: sdata,spos: null};   
    }       
};

/*        TS Data structure      */

function timeSeriesData(f) {
    this.startIndex = 0;
    this.endIndex = 0;
    this.exch="";
    this.ticker="";
    this.symbol ="";
    this.frequency = f;
    this.priceData = []; // the price time series Data.
    this.volumeData = [];
	this.fairValueData = []; // the fairvalue chart data for .com.
	this.fundamentalData = {PE:[],PB:[],PC:[],PS:[],FairValue:[],REPS:[],SInterest:[],MReturnIndex:[]}; // ratio data.
	this.ready = false;
};

timeSeriesData.prototype = {
    getDividendAndSplitDataSegment: function(stIndex,endIndex,dataType,isD,isS) {
        var dty = isD?1:-1;  // 1: dividend 2: split
        var sty =  isS?2:-1;
        var data, source = (dataType=="p")? this.priceData:this.volumeData;
        if (source!=null&&source.length>1) {
            data = [];
        } else {
            return null;
        }
        var st = this.startIndex , end =  this.endIndex;
        for (var j=0;j<source.length;j++) {
            if (source[j].DateIndex <stIndex || source[j].DateIndex > endIndex) {
                continue;
            }
            if (source[j].Type == dty || source[j].Type == sty) {
                data.push(source[j]);
            }
        }
        return data;
    },
    
    getDataSegment :function(sIndex,eIndex,isFilled,dataType,dx,type,rfData) {        // get the price,volume or fundamental data segment.
        var fre  = this.frequency; 
        var data, source = null;
		if(dataType=="p"){
			source = this.priceData;
		} else if(dataType=="postTax"){
            source = this.postTaxReturn;
        } else if(dataType=="v"){
			source = this.volumeData;
		} else if(dataType == "f"){   //fair value data; 
			source = this.fairValueData;
		} else if(this.fundamentalData[dataType] != undefined){ // ratio data.
			source = this.fundamentalData[dataType];
		}
        if (source != null && source.length > 1) {
            data = [];
        } else {
            return {data: null,spos: null,freq:fre};
        }
		var stIndex = sIndex, endIndex = eIndex;
        var st = source[0][source[0].length-1], end = source[source.length-1][source[source.length-1].length-1];
		if(stIndex==null)stIndex = st;
		if(endIndex == null) endIndex = end;
		var tyl = type.split("_");
		var type = tyl[0];
		var isTenK = (tyl.length>1&&tyl[1]=="k");
        var interVal = 1;
        if (fre == "d"|| fre =="minute") {
            interVal = 1;
        } else if (fre == "w") {
            interVal = 7;
        } else if (fre == "m") {
            interVal = 30;
        }
        var inx = 0,spos = null,pre = isTenK?10000:100,ad=isTenK?10000:0;
        
        if (isFilled && stIndex < st) {   
           for (var i = stIndex;i<st;i+=interVal) {
                var dp;
                if(rfData!=null) interVal = rfData[inx+1][rfData[inx+1].length-1]-rfData[inx][rfData[inx].length-1];  // fill data accord to the rfData.
                if (dataType == "p") { 
                    dp = [inx,NaN,NaN,NaN,NaN,NaN,i];
                } else {
                    dp = [inx,NaN,i];
                }
                data.push(dp);
                inx++;
           }     
        }
        for (var j = 0; j < source.length; j += dx) {
            if (source[j][source[j].length-1] < stIndex || source[j][source[j].length-1] > endIndex) {
                continue;
            }
			if((type=="return"||type=="return4")&&spos == null && !isNaN(source[j][1])){
				spos = source[j][1];
			}
            var dpp = [];
            dpp.push(inx);
			if((type=="return"||type=="return4") && spos !=null){    // convert to return value.
				dpp.push(ad+pre*(source[j][1]-spos)/spos);
			}else{
				dpp.push(source[j][1])
			}
            for(var h=2;h<source[j].length;h++){
				if(type=="return4" && spos !=null&&h<5){  // OHLC convert to return price.
					dpp.push(ad+pre*(source[j][h]-spos)/spos);
				}else{
					dpp.push(source[j][h]);
				}
			} 
            data.push(dpp);
            inx++;
        }
        if (isFilled && endIndex > end) {   
           for (var g = end; g < endIndex; g += interVal) {
                var d;
                if (dataType=="p") {
                    d = [inx,NaN,NaN,NaN,NaN,NaN,g+interVal];
                } else {
                d = [inx,NaN,g+interVal];
                }
                data.push(d);
                inx++;
           }     
        }
        return {data: data,spos: spos,freq:fre};
    }
};
    
    /*   data models   store the TS and intrayday data */
function quoteData(symbol) {
	this.symbol = symbol;
    this.intradayData = {1:new intrayData(1),
						 5:new intrayData(5),
						 10:new intrayData(10)
	}
	this.drawings = {};  // store the drawings for one ticker.
    this.dailyData = new timeSeriesData("d");
    this.weeklyData = new timeSeriesData("w");
    this.monthlyData = new timeSeriesData("m");
    this.dividendAndSplitData = new timeSeriesData("d");
	this.earningsData  = new timeSeriesData("d");
	this.indicatorData = {SMAV:{},SMA:{},EMA:{},BBands:{},MACD: {},RSI:{},
		      SStochastic:{},FStochastic:{},ROC:{},WillR:{},PSAR:{},PChannel:{},MAE:{},
			  ULT:{},Momentum:{},DMI:{},UDRatio:{},OBV:{},VAcc:{},VPlus:{},MFI:{},DYield:{},
			  FVolatility:{},WMA:{},AccDis:{},Mass:{},Volatility:{},ForceIndex:{},ATR:{},Keltner:{},PrevClose:{}
	}
};
    
quoteData.prototype = {
    getDataByType: function(ty) {
        if (!isNaN(ty)) { // 	intrayday data
            return this.intradayData[ty];
        } else if (ty=="d") {
            return this.dailyData;
        } else if(ty=="w") {
            return this.weeklyData;
        } else if(ty=="m") {
            return this.monthlyData;
        } else if(ty=="s") {
            return this.dividendAndSplitData;
        } else {
            return null;
        }
    },
	getRollDividend:function(freq,stIndex,edIndex){ //jacky 3.25
		var data = this.getDataByType(freq);
		var da = this.getDataByType("s");
		if(data==null)return[];
		var pData = data.getDataSegment(stIndex,edIndex,true,"p",1,"price").data;
		var dData = da. getDividendAndSplitDataSegment(stIndex,edIndex,"p",true,false);
		var dList = [];
        if (dData == null ||dData.length < 1||pData == null || pData.length<1) {
            return {data:[],spos:null};
        }
        var k = 0,ty,temp = parseFloat(dData[0].Desc);
        for(var i = 0; i < pData.length; i++) {  
            var dp = [];      
            var ls = pData[0][pData[0].length-1];
			if (i > 0) {
               ls = pData[i-1][pData[i-1].length-1]; 
            }
			var cur = pData[i][pData[i].length-1];
			var flag = false,d;
            for ( j = k; j < dData.length; j++) {  
				d = dData[j].DateIndex;  
                if (d <= cur && d > ls) {
                    dp.push(pData[i][0]);
					temp = parseFloat(dData[j].Desc)
					dp.push(temp); 
                    k=j;
					flag = true;
                    break;
                }
            }
            if (flag) {// find the dividend data.      
               dp.push(cur);     
            }else{
				dp.push(pData[i][0])
				dp.push(temp);
				dp.push(cur);
			}
			dList.push(dp);   
        }
		return {data:dList,spos:null};
	},
	getIndicatorDataSegment:function(indicatorName,ty,arg,stIndex,edIndex,days,vtype,price,freq){  // freq is for intray data. such as 1,5
		if(!this.indicatorData[indicatorName] || !arg){// not support this indicator
			return {data:[],spos: null};
		}
		var indData = this.indicatorData[indicatorName],sIndex=stIndex,eIndex = edIndex;
		var ckey = ty+'_'+(!isNaN(ty)?(freq+"_"):"")+ (arg&&arg.length?arg.join("_"):"");
		var spos = null;
		if(!isNaN(ty) || typeof(indData[ckey]) == "undefined" || indData[ckey]==null){   // there is no data in cache. intraday data.
			var mData = this.getDataByType(ty),pData,vData,dData,isFilled = true;
			if(mData==null){
				return {data: [],spos: null};
			}
			if(!isNaN(ty)){  //intraday data.
				if (typeof(price) == "undefined" || price == null||price.spos!=null) {  //if price is not return data.
					var dda = mData.getPriceData("price", days,stIndex,edIndex,freq);
					pData = dda.data, spos = dda.spos;
				}else{
					pData = price.data, spos = price.spos;
				}
				if(pData.length>0){
					sIndex = pData[0][pData[0].length-1];
					eIndex = pData[pData.length-1][pData[pData.length-1].length-1];
				}
				if(indicatorName=="SMAV"||indicatorName=="UDRatio"||indicatorName=="OBV"||indicatorName=="MFI"||indicatorName=="VAcc"||indicatorName=="VPlus"||indicatorName=="VBP"||indicatorName=="AccDis"||indicatorName=="ForceIndex"){
					vData = mData.getVolumeData(days,stIndex,edIndex,freq).data; 
				}
				isFilled = false;
			}else{
				pData = mData.priceData;
				vData = mData.volumeData;
			}
			if(indicatorName == "DYield")dData = this.dividendAndSplitData.priceData;
			if(indicatorName =="PrevClose"){
				indData[ckey] = this._getPrevCloseData(pData);
			}else{
				indData[ckey] = this._calculateIndicator(indicatorName,pData,vData,dData,ty,arg); // calculate indicator data.
			}
		}
		var da = [];
		if(indicatorName == "PrevClose")return {data: indData[ckey],spos: null};
		if(indData[ckey]&& indData[ckey].length){
			for(var i=0;i<indData[ckey].length;i++){
				da.push(techIndicatorCal.filtrateCalculatedData(sIndex,eIndex,isFilled,indData[ckey][i],ty,vtype,spos));
			}
			return da.length>1?da:da[0];
		}
		return {data: [],spos: null};
	},
	_getPrevCloseData:function(pList){
		var d = [];
		if(this.intradayData&&this.intradayData[1]&&this.intradayData[1].data&&this.intradayData[1].data.length){
			var list = typeof(pList)== "undefined"?this.intradayData[1].data[0].price:pList;
			var prevClose = this.intradayData[1].data[0].yestClosePrice;
			if(typeof(prevClose) != "undefined"&&prevClose != 0){
				for(var i=0; i<list.length;i++){
	                var t = [list[i][0],prevClose,list[i][list[i].length-1]];
					d.push(t);
	            }
			}		
		}
		return d;
	},
	_calculateIndicator:function(indicatorName,pData,vData,dData,ty,arg){
		var indData;
		switch(indicatorName){
				case 'SMA':{
					indData = [techIndicatorCal.calculateSma(pData,ty,arg[0])];
				}break;
				case 'EMA':{
					indData = [techIndicatorCal.calculateEma(pData,ty,arg[0])];
				}break;
				case 'BBands':{
					indData= techIndicatorCal.calculateBBands(pData,ty,arg[0],arg[1]);
				}break;
				case 'MACD':{
					indData = techIndicatorCal.calculateMACD(pData,ty,arg[0],arg[1],arg[2]);
				}break;
				case 'RSI':{
					indData = [techIndicatorCal.calculateRSI(pData,ty,arg[0])];
				}break;
				case 'ROC':{
					indData = [techIndicatorCal.calculateROC(pData,ty,arg[0])];
				}break;
				case 'SStochastic':{
					indData = techIndicatorCal.calculateSlowStochastic(pData,ty,arg[0],arg[1]);
				}break;
				case 'FStochastic':{
					indData = techIndicatorCal.calculateFastStochastic(pData,ty,arg[0],arg[1]);
				}break;
				case 'PSAR':{
					indData = [techIndicatorCal.calculatePSAR(pData,ty,arg[0],arg[1])];
				}break;
				case 'WillR':{
					indData = [techIndicatorCal.calculateWillR(pData,ty,arg[0])];
				}break;
				case 'SMAV':{
					indData = [techIndicatorCal.calculateSma(vData,ty,arg[0])];
				}break;
				case 'DMI':{
					indData = techIndicatorCal.calculateDmi(pData,ty,arg[0]);
				}break;
				case 'PChannel':{
					indData = techIndicatorCal.calculatePriceChannel(pData,ty,arg[0]);
				}break;
				case 'MAE':{
					indData = techIndicatorCal.calculateMAEnvelope(pData,ty,arg[0],arg[1]);
				}break;
				case 'Momentum':{
					indData = [techIndicatorCal.calculateMomentum(pData,ty,arg[0])];
				}break;
				case 'ULT':{
					indData = [techIndicatorCal.calculateUlt(pData,ty,arg[0],arg[1])];
				}break;
				case 'MFI':{
					indData = [techIndicatorCal.calculateMFI(pData,vData,ty,arg[0])];
				}break;
				case 'VAcc':{
					indData = [techIndicatorCal.calculateVolumeAcc(pData,vData,ty,arg[0])];
				}break;
				case 'UDRatio':{
					indData = [techIndicatorCal.calculateUpDownRatio(pData,vData,ty,arg[0],arg[1])];
				}break;
				case 'OBV':{
					indData = [techIndicatorCal.calculateObv(pData,vData,ty,arg[0])];
				}break;
				case 'VPlus':{
					indData = [techIndicatorCal.calculateVolPlus(pData,vData,ty,arg[0])];
				}break;
				case 'VBP':{
					indData = [techIndicatorCal.calculateVolByPrice(pData,vData,ty,arg[0])];
				}break;
				case 'DYield':{
					indData = [techIndicatorCal.calculateDividendYield(pData,dData,ty)];
				}break;
				case 'FVolatility':{
					indData = [techIndicatorCal.calculateFVolatility(pData,ty)];
				} break;
				case 'WMA':{
					indData = [techIndicatorCal.calculateWMA(pData,ty,arg[0])];
				} break;
				case 'AccDis':{
					indData = [techIndicatorCal.calculateAccDis(pData,vData,ty)];
				} break;
				case 'Mass':{
					indData = [techIndicatorCal.calculateMass(pData,ty)];
				} break;
				case 'Volatility':{
					indData = [techIndicatorCal.calculateVolatility(pData,ty,arg[0],arg[1])];
				} break;
				case 'ForceIndex':{
					indData = [techIndicatorCal.calculateForceIndex(pData,vData,ty,arg[0])];
				} break;
				case 'ATR':{
					indData = [techIndicatorCal.calculateAtr(pData,ty)];
				} break;
				case 'Keltner':{
					indData = techIndicatorCal.calculateKeltner(pData,ty,arg[0],arg[1],arg[2]);
				} break;
				default:{
            	}break;
			}
			return indData;
	},
	_addTTMdata:function(da){
		if(da.ready&&da.ttm&&da.ttm.dateIndex&&da.priceData&&da.priceData.length&&da.ttm.dateIndex>da.endIndex){  // add last trade date to daily data.
            var ttmprice = da.ttm.price,lggg = da.priceData.length;lastp = da.priceData[da.priceData.length-1][1];
            var ttmfalg = ttmprice[0]>lastp?1:0;
            da.priceData.push([lggg,ttmprice[0],ttmprice[1],ttmprice[2],ttmprice[3],ttmfalg,da.ttm.dateIndex]);
            da.volumeData.push([lggg,da.ttm.vol/1000000,ttmfalg,da.ttm.dateIndex]);
            da.endIndex = da.ttm.dateIndex;
        }
	},
    saveTimeSeriesData: function(data,freq,type) {  // type is security type. 20: forex.
		var inxlist = data.Index, plist = data.Price, vlist = data.Volume, dlist= data.DividendSplit;
        var da = this.getDataByType(freq);
        if (da == null||!inxlist) {
			da.ready =true;
            return;
        }
		da.ready = false;
        da.priceData = [];
        da.volumeData = [];
        da.startIndex = inxlist[0]; var s =0;
        if (inxlist.length > 4 && plist[0].length < 2 && inxlist[1]-inxlist[0] > inxlist[2]-inxlist[1]) {//remove the first point.
            s=1;da.startIndex = inxlist[1]; 
        }
        da.endIndex = inxlist[inxlist.length-1];
		var flag = 1;
		var isLondonStock = this.type==1&&(this.exch==151||this.exch=="152")&& QSAPI.DataManager.Chart.currencyForUKExchange=="GBX";  //if or not convert "GBP" to "GBX"
        for (var i = s; i < plist.length; i++) { 
            var dp = [];
            if (inxlist[i] <= 0) {
                continue;
            }
            dp.push(i-s);
			if(plist[i]&&plist[i].length == 1){   // only close price.
				dp = dp.concat([plist[i][0],NaN,NaN,NaN]);
			}else if(!plist[i]||plist[i].length<4){  // invild data points
				dp = dp.concat([NaN,NaN,NaN,NaN]);
			}else{
				if(isLondonStock){
				    for(var ii=0;ii<plist[i].length;ii++){
						dp.push(plist[i][ii]*100); // convert the "GBP" to "GBX";
					}
				}else{
					dp = dp.concat(plist[i]); // push the histrical price: close, high,low open.
				}
			} 
            if (i > 0) { 
				flag = plist[i][0] >=plist[i-1][0] ?1:0                                                   //1 :up, 0 down. this field is for drawing candlestick chart.
            } else {
                flag = 1;
            }
			dp.push(flag);
            dp.push(inxlist[i]);       // push dateIndex value;
            da.priceData.push(dp);
            if (type!=20&&vlist != null && i < vlist.length) {  // forex not save volume data.
                da.volumeData.push([i-s,vlist[i]/1000000,flag,inxlist[i]]);
            }
        }
		da.ready = true;
		var st = 0 , dIndex, dd = this.dividendAndSplitData;  //save the dividend and split data.
        if (dd.startIndex != null) st = dd.startIndex;
		dd.ready = !data.hasDS?false:true;
		if(dlist!=null&&dlist.length){
			for(var i = 0; i < dlist.length; i++) {    
				dIndex = parseInt(dlist[i].D,10);
	            if (i==0) { 
	                if (st != 0 && dIndex >= st) {
	                    break;
	                } else { 
	                    dd.startIndex = dIndex;
	                    dd.priceData = [];
	                } 
	            }
	            if (i == dlist.length-1) {
	                dd.endIndex = dIndex;
	            }
	            dd.priceData.push({DateIndex:dIndex,Desc:dlist[i].V,Type:dlist[i].T}); // 1:dividend,2:split.
	        }
			dd.ready = true;
		}	
		this._addTTMdata(da);
    },
	saveFundData:function(data,freq){
        var da = this.getDataByType(freq);
        if (data == null||!data.Index) {
			da.ready = true;
            return;
        }
		var indexList = data.Index,navList = data["1"],dlist = data["1578"],postTaxReturn = data["15"];
		da.startIndex = indexList[0],da.endIndex = indexList[indexList.length-1];
        if(navList&&navList.length){
            da.priceData = [];
            for(var i=0;i<indexList.length;i++){
                if(indexList[i]<0)continue;
                da.priceData.push([i,navList[i],indexList[i]]);
            }
            da.ready = true;
        }
        if(postTaxReturn&&postTaxReturn.length){
            da.postTaxReturn = [];
             for(var i=0;i<indexList.length;i++){
                if(indexList[i]<0)continue;
                da.postTaxReturn.push([i,postTaxReturn[i],indexList[i]]);
            }
            da.pTaxReady = true;
        }	
		if(dlist&&dlist.length){  // save dividend data.
		    var st =0, dd = this.dividendAndSplitData;
			if (dd.startIndex != null) st = dd.startIndex;
			for(var i = 0; i < dlist.length; i++) {    
                dIndex = parseInt(dlist[i].D,10);
                if (i==0) { 
                    if (st != 0 && dIndex >= st) {  // has exsit.
                        break;
                    } else { 
                        dd.startIndex = dIndex;
                        dd.priceData = [];
                    } 
                }
                if (i == dlist.length-1) {
                    dd.endIndex = dIndex;
                }
                dd.priceData.push({DateIndex:dIndex,Desc:dlist[i].V,Type:1}); // 1:dividend,2:split.
            }
		}
	},
	saveFundamentalData:function(data,freq,sName){
		var inxlist = data.date, plist = data.value;
		var da = this.getDataByType(freq);
        if (da == null || !da.fundamentalData[sName]) {
            return;
        }
		da.fundamentalData[sName] = [];
		var s =0;
        for (var i = s; i < plist.length; i++) {
			var dp = [];
			if (inxlist[i] <= 0) {
				continue;
			}
			dp.push(i - s);
			dp.push(plist[i]); // push the histrical price: close, high,low open.			
			dp.push(inxlist[i]); // push dateIndex value;
			da.fundamentalData[sName].push(dp);
		}
	},
	saveEarningsData:function(data){
		var inxlist = data.date, elist = data.value;
		this.earningsData.ready = true;
		var da = this.earningsData;
		for (var i = 0; i < elist.length; i++) {
			var dp = [];
			if (inxlist[i] <= 0) {
				continue;
			}
			dp.push(i);
			dp.push(elist[i]); // push earnings.
			if(i>3){
				dp.push(elist[i]>=elist[i-4]?1:0);  // 1: up, 0:down
			}else{
				dp.push(1);
			}			
			dp.push(inxlist[i]); // push dateIndex value;
			da.priceData.push(dp);
		}
	},
	saveIntradayData:function(data,freq){  //
		if (!$.isArray(data)||data == null || !data.length) {  // no data for this symbols.
			this.intradayData[freq].ready = true;
			return;
		}
		var ty = typeof(data[0].timeInterval)=="undefined"?1:data[0].timeInterval;
		if(this.intradayData[ty] == null) this.intradayData[ty] = new intrayData(ty);
        this.intradayData[ty].openTime = data[0].startTime;
        this.intradayData[ty].closeTime = data[0].endTime;
		this.intradayData[ty].exch =  data[0].exchangeId;
		this.intradayData[ty].ticker = data[0].symbol.toUpperCase();
		this.intradayData[ty].data = [];
		this.intradayData[ty].ready = false;
		var plist,vlist,pl,vl,fillFlag;
		var pointNum = Math.floor((data[0].endTime-data[0].startTime)/ty)+1,ot = this.intradayData[ty].openTime;
		var o=NaN,h=0,l=100000000,c=NaN,dInx,v;
        //var cdx = this.exch=="152"||this.exch=="151"?0.01:1;
		var cdx = (this.type==1&&(this.exch==151||this.exch=="152")&& QSAPI.DataManager.Chart.currencyForUKExchange=="GBP")?0.01:1; //convert to GBP
		for(var i = 0;i < data.length;i++){
			plist = data[i].security=="20"?data[i].bidPrice:data[i].lastPrice;
			if(freq==1&&data[i].security=="20"&&i>=5) continue;  // only store five days data when frequency==1 min and it is Forex.
			vlist = data[i].volume;
			var dayData = {};
			dayData.date = RTQ_ChartUtil.convertToDate(data[i].date);
			dayData.yestClosePrice = data[i].yesterdayClosePrice*cdx;
			dayData.accVolume = [];
			dayData.price = [];
			pl = plist.length, vl = vlist.length, temp=dayData.yestClosePrice,isup=0,fillFlag = data[i].fillFlag;
			if (freq==1&&i == 0&&plist&&plist.length) {
				o = plist[0][3],dInx = RTQ_ChartUtil.toDateIndex(dayData.date);
			}
			var flg = 0;
			for(var j=0;j<pointNum;j++){
			   if (j < pl && plist[j].length) {
			   		isup = plist[j][0]*cdx>temp?1:0;
					temp = plist[j][0]*cdx;
					flg = typeof(fillFlag)=="undefined"?0:fillFlag[j];
	                dayData.price[j] = [j,plist[j][0]*cdx,plist[j][1]*cdx,plist[j][2]*cdx,plist[j][3]*cdx,isup,flg,ot+j*ty]; // store price data.
	                if(freq==1&&i==0){  // caculate the CHLO price for last trade date.
						if(!isNaN(plist[j][0]))c = plist[j][0];
						if(plist[j][1]>h)h=plist[j][1];
						if(plist[j][2]<l)l=plist[j][2];
					}
	           } else {
	               dayData.price[j] = [j,NaN,NaN,NaN,NaN,0,1,ot+j*ty];
	           }
	           if (j < vl&&vlist[j]>0) {
	               dayData.accVolume[j] = [j,vlist[j],isup,ot+j*ty]; // store volume data
	               if(i==0&&vlist[j]>0)v = vlist[j];
	           } else {
	               dayData.accVolume[j] = [j,NaN,0,ot+j*ty];
	           }
			}
			if(freq==1&&i==0)this.dailyData.ttm={dateIndex:dInx,price:[c*cdx,h*cdx,l*cdx,o*cdx],vol:v}; // store the lastest trade day data to daily data.
			this.intradayData[ty].data.push(dayData);
		}
		this._addTTMdata(this.dailyData);
		this.intradayData[ty].ready = true;   
		if(ty==1&&this.intradayData[ty].data.length && this.intradayData[ty].data[0].date){  // update the lastestTradeDate.
			//this.lastTradeDate = this.intradayData[ty].data[0].date;
			this.intradayData.lastTradeDate = this.intradayData[ty].data[0].date;
			if(QSAPI.DataManager.Chart)QSAPI.DataManager.Chart._refreshChartLastTradeDate(this.symbol,this.intradayData[ty].data[0].date,this.intradayData[ty].data[this.intradayData[ty].data.length-1].date);
		}
    },
	removeDrawing:function(type,point){
		var removed = false;
		if(this.drawings[type]){
			for(var i=0;i<this.drawings[type].length;i++){
				var d = this.drawings[type][i];
				if(type=="levelline"){
					if(d.start.y==point.start.y){
						this.drawings[type].splice(i,1);
						removed = true;
						break;
					}
				}else if(d.start.dIdx==point.start.dIdx&&d.end.dIdx==point.end.dIdx&&d.start.y==point.start.y&&d.end.y==point.end.y){
					this.drawings[type].splice(i,1);
					removed= true;
					break;
				}
			}
		}
		if(GB_ChartDataManager.saveDrawings&&removed){  // remove the datapoint from DB.
			var url = GB_ChartDataManager.dataUrls.drawings;
			var data = {action:"removedraw",
						drawid:point.id
					   } 
		    $.post(url,data,function(data){
				
			});
		}
	},
	saveLastDrawing:function(drawingType,point){  //insert one drawing
		if(!this.drawings[drawingType]){
			this.drawings[drawingType] = [];
		}
		point.drawType = drawingType;
		this.drawings[drawingType].push(point);	
		if(GB_ChartDataManager.saveDrawings){  // insert the drawing to DB
			var url = GB_ChartDataManager.dataUrls.drawings;
			delete point.oldStart,delete point.oldEnd;
			var info = $.toJSON(point);
			var data = {action:"insertdraw",
						pid:this.symbol,
						saveinfo:info
					   }
			var self = this;
			$.post(url,data,function(data){
				var jData = $.evalJSON(data);
				if(jData.secId == self.symbol){  
					point.id = jData.list[0].eventId;
				}
			})
		}
	},
	updateDrawing:function(type,point){
		if(type!="levelline"&&(!point.oldStart||!point.oldEnd)) return;
		var upadted = false;
		if(this.drawings[type]){
			for(var i=0;i<this.drawings[type].length;i++){
				var d = this.drawings[type][i];
				if(type=="levelline"){  // update the levelline.
					if(d.start&&point.oldStart&&Math.abs(d.start.y-point.oldStart.y)<0.0001){  // when the differ<0.0001 we regard the two digtils is equal.
						this.drawings[type][i].start = $.extend(true,{},point.start);
						upadted = true
						break
					}
				}else if(d.start.dIdx==point.oldStart.dIdx&&d.end.dIdx==point.oldEnd.dIdx&&Math.abs(d.start.y-point.oldStart.y)<0.0001&&Math.abs(d.end.y-point.oldEnd.y)<0.0001){
					this.drawings[type][i].start = $.extend(true,{},point.start);
					this.drawings[type][i].end = $.extend(true,{},point.end);
					upadted = true;
					break;
				}
			}
		}
		if(GB_ChartDataManager.saveDrawings&&upadted){  // update the drawing to DB
			var url = GB_ChartDataManager.dataUrls.drawings;
			infoObj = {drawType:point.drawType,start:point.start,end:point.end};
			var info = $.toJSON(infoObj);
			var data = {action:"updatedraw",
						pid:this.symbol,
						drawid:point.id,
						saveinfo:info
					   }
			$.post(url,data,function(data){
				
			})
		}
	},
	getDrawings:function(dataPoints,callback){
		if(GB_ChartDataManager.saveDrawings){//save drawings to DB.
			if(this.drawingReady){
				if(typeof(callback)=="function"){
					callback(this._getDrawings(dataPoints));
				}
			}else{
				var url = GB_ChartDataManager.dataUrls.drawings+"action=loaddraw&pid="+this.symbol;
				var self = this;
				$.get(url,function(data){
					var jData = $.evalJSON(data);
					if(jData.secId == self.symbol&&jData.list&&jData.list.length){  // store the drawings 
						self.drawingReady = true;
						self._storeDrawings(jData.list);
						if(typeof(callback)=="function"){
							callback(self._getDrawings(dataPoints));
						}
					}
				})
			}
		}else{
			if(typeof(callback)=="function"){
				callback(this._getDrawings(dataPoints));
			}
		}
	},
	_storeDrawings:function(list){
		for(var i=0;i<list.length;i++){
			var dp = $.evalJSON(list[i].eventInfo); // one drawing.
			if(!this.drawings[dp.drawType]) this.drawings[dp.drawType]=[];
			dp.id = list[i].eventId;
			this.drawings[dp.drawType].push(dp);
		}
	},
	_getDrawings:function(dataPoints){  // 
		var ds = {};
		var sIndex = dataPoints[0][dataPoints[0].length-1],eIndex = dataPoints[dataPoints.length-1][dataPoints[dataPoints.length-1].length-1];
		for(var o in this.drawings){
			if(this.drawings[o].length){
				ds[o] = [];
				for(var i=0;i<this.drawings[o].length;i++){
					if(o=="levelline"){  //return the all levelline to chart.
						ds[o].push($.extend(true,{},this.drawings[o][i]));
					}else if(this.drawings[o][i].start.dIdx>=sIndex&&this.drawings[o][i].end.dIdx<=eIndex){
						var point = $.extend(true,{},this.drawings[o][i]);
						var findStart = false;
						for(var j=0;j<dataPoints.length;j++){
							if(dataPoints[j][dataPoints[j].length-1]==point.start.dIdx){
								point.start.x = dataPoints[j][0];
								findStart = true;
							}else if(dataPoints[j][dataPoints[j].length-1]==point.end.dIdx&&findStart){
								point.end.x = dataPoints[j][0];
								ds[o].push(point);
								break;
							}
						}
					}
				}
			}
		}
		return ds;
	}
};
GB_ChartDataManager = QSAPI.DataManager.Chart = new RTQ_ChartDataManager({});
